/************************************************************************

This is a tutorial example that shows how the parser and the C++
classes DMIS can be used to count the total number of times each
of a set of kinds of nominal feature is defined in a set of DMIS
input files.

To prevent name conflicts between your code and code from the NIST
DMIS Test Suite, all of the code in the Test Suite is in the namespace
NDTS. To use the NDTS code, a "namespace NDTS" declaration is needed
at the beginning of your code as shown below. The namespace declaration
lists the NDTS variables and functions that will appear in your code
that are not in dmis.hh. When they are used in your code, they must
be prefixed by NDTS:: . 

See the Makefile that accompanies this file for details of how the
executable is compiled.

************************************************************************/

#include <string.h>  // for strlen, strcpy, strcat
#include <stdio.h>   // for fopen, etc.
#include <stdlib.h>  // for exit
#include "dmis.hh"

#define PRENAME "PrEpRoCeSsDmIs"

/************************************************************************/

namespace NDTS {
extern FILE * yyin;                                     // in dmisLex.cc
extern int numErrors;                                   // in dmisYACC.cc
extern int numWarnings;                                 // in dmisYACC.cc
extern inputFile * tree;                                // in dmisYACC.cc

void preprocess(char * fileNameIn);                     // in dmisYACC.cc
void resetParser();                                     // in dmisYACC.cc
int yyparse();                                          // in dmisYACC.cc
}

void analyzeItems(std::list<NDTS::dmisItem *> * items); // in this file
void analyzeManyFiles(char * fileNameFile);             // in this file
void analyzeOneFile(char * fileName);                   // in this file
int main(int argc, char * argv[]);                      // in this file
void reportResults();                                   // in this file

int arcs = 0;
int circles = 0;
int cones = 0;
int cparlns = 0;
int cylndrs = 0;
int ellipses = 0;
int elongcyls = 0;
int lines = 0;
int parplns = 0;
int planes = 0;
int points = 0;
int spheres = 0;

/************************************************************************/

/* analyzeItems

Returned Value: none

Called By: analyzeManyFiles

This adds 1 to the number of instances of a type of feature whenever
that type of feature is found among the dmisItems being analyzed.

To determe if a pointer to an instance (inst1) of one class (class1) is
also a pointer to an instance of another class (class2) this uses the
isA macro, called in the form isA(inst1, class2). The isA macro is
an alias for using dynamic_cast in the form dynamic_cast<class2 *>(inst1).

Some dmisItems are blocks (such as an ifBlock or a caseBlock). This
function is not looking at the dmisItems inside of blocks, so if a
feature is defined inside a block, it does not get counted.

*/

void analyzeItems(                    /* ARGUMENTS                    */
 std::list<NDTS::dmisItem *> * items) /* list of dmisItems to examine */
{
  std::list<NDTS::dmisItem *>::iterator iter;

  for (iter = items->begin(); iter != items->end(); iter++)
    {
      if (isA((*iter), NDTS::featArc1Nom))
	arcs++;
      else if (isA((*iter), NDTS::featArc2Nom))
	arcs++;
      else if (isA((*iter), NDTS::featCircleNom))
	circles++;
      else if (isA((*iter), NDTS::featConeNom))
	cones++;
      else if (isA((*iter), NDTS::featCparlnNom))
	cparlns++;
      else if (isA((*iter), NDTS::featCylndrNom))
	cylndrs++;
      else if (isA((*iter), NDTS::featEllipsNom))
	ellipses++;
      else if (isA((*iter), NDTS::featElongcylNom))
	elongcyls++;
      else if (isA((*iter), NDTS::featLineNom))
	lines++;
      else if (isA((*iter), NDTS::featParplnNom))
	parplns++;
      else if (isA((*iter), NDTS::featPlaneNom))
	planes++;
      else if (isA((*iter), NDTS::featPointNom))
	points++;
      else if (isA((*iter), NDTS::featSphereNom))
	spheres++;
    }
}

/************************************************************************/

/* analyzeManyFiles

Returned Value: none

Called By: main

This expects that the file whose name is fileNameFile will contain the
names of a number of DMIS input files.

For each file listed in the fileNameFile, this parses the file,
reports errors and warnings, and then (if there were no errors or
warnings and there are items to analyze) calls analyzeItems.

*/

void analyzeManyFiles( /* ARGUMENTS                                     */
 char * fileNameFile)  /* name of file containing DMIS input file names */
{
  FILE * fileList;
  static char fileName[256];
  int nameLength;

  fileList = fopen(fileNameFile, "r");
  if (fileList == 0)
    {
      fprintf(stderr, "unable to open file %s for reading\n",
              fileNameFile);
      exit(1);
    }
  while (fgets(fileName, 256, fileList))
    {
      nameLength = strlen(fileName);
      if (nameLength == 255)
	{
	  fprintf(stderr, "file name too long: %s\n", fileName);
	  exit(1);
        }
      while ((fileName[nameLength - 1] == 10) ||
             (fileName[nameLength - 1] == 13))
	{ // get rid of the end of line character(s)
	  fileName[nameLength - 1] = 0;
	  nameLength--;
	}
      if (strcmp((fileName + nameLength - 4), ".dmi"))
	{
	  fprintf(stderr, "file name does not end in .dmi: %s\n",
                  fileName);
	  exit(1);
	}
      printf("*****************************************\n\n");
      printf("%s\n\n", fileName);
      analyzeOneFile(fileName);
    }
  fclose(fileList);
}

/************************************************************************/

/* analyzeOneFile

Returned Value: none

Called By: analyzeManyFiles

This:
1. preprocesses the file whose name is fileName.
2. opens the preprocessed file and sets yyin to the opened file.
3. exits if the preprocessed file did not open.
4. parses the preprocessed file (which builds a parse tree).
5. closes yyin.
6. deletes the preprocessed file.
7. reports the number of errors and warnings.
8. calls analyzeItems if there were no errors or warnings and there are
   items to analyze.
9. resets the parser so it is ready to parse another file.

*/

void analyzeOneFile( /* ARGUMENTS               */
 char * fileName)    /* name of DMIS input file */
{
  NDTS::preprocess(fileName);
  NDTS::yyin = fopen(PRENAME, "rb");
  if (NDTS::yyin == 0)
    {
      fprintf(stderr, "unable to open file %s for reading\n", PRENAME);
      exit(1);
    }
  NDTS::yyparse();
  fclose(NDTS::yyin);
  remove(PRENAME);
  printf("%d error%s\n", NDTS::numErrors, ((NDTS::numErrors == 1) ? "" : "s"));
  printf("%d warning%s\n\n", NDTS::numWarnings,
	  ((NDTS::numWarnings == 1) ? "" : "s"));
  if ((NDTS::numErrors == 0) && (NDTS::numWarnings == 0))
    {
      if (NDTS::tree->get_dmisItemList())
	analyzeItems(NDTS::tree->get_dmisItemList());
    }
  NDTS::resetParser();
}

/************************************************************************/

/* main

This calls analyzeManyFiles to count the number of instances of each
feature type used in a set of DMIS input files, and then calls
reportResults to print the results.

*/

int main(       /* ARGUMENTS                         */
 int argc,      /* one more than number of arguments */
 char * argv[]) /* command name and arguments        */
{
  if (argc != 2)
    {
      fprintf(stderr, "usage: %s <file name>\n", argv[0]);
      exit (1);
    }
  analyzeManyFiles(argv[1]);
  reportResults();
  return 0;
}

/************************************************************************/

/* reportResults

Returned Value: none

Called By: main

For each nominal feature type for which data was collected, this prints
the number of times a statement defining a nominal feature of that type
appeared in one of the DMIS input files.

*/

void reportResults() /* NO ARGUMENTS */
{
  printf("nominal features found\n");
  printf("%d FEAT/ARC\n", arcs);
  printf("%d FEAT/CIRCLE\n", circles);
  printf("%d FEAT/CONE\n", cones);
  printf("%d FEAT/CPARLN\n", cparlns);
  printf("%d FEAT/CYLNDR\n", cylndrs);
  printf("%d FEAT/ELLIPS\n", ellipses);
  printf("%d FEAT/ELONGCYL\n", elongcyls);
  printf("%d FEAT/LINE\n", lines);
  printf("%d FEAT/PARPLN\n", parplns);
  printf("%d FEAT/PLANE\n", planes);
  printf("%d FEAT/POINT\n", points);
  printf("%d FEAT/SPHERE\n", spheres);
}

/************************************************************************/

